package com.shopnight.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.shopnight.config.WxPayConfig;
import com.shopnight.constant.ShopIndentConstant;
import com.shopnight.entity.*;
import com.shopnight.interceptors.JWTInterceptor;
import com.shopnight.mapper.AllOrderMapper;
import com.shopnight.mapper.RiderMapper;
import com.shopnight.mapper.UserMapper;
import com.shopnight.result.ApiResult;
import com.shopnight.service.ErrandOrderService;
import com.shopnight.mapper.ErrandOrderMapper;
import com.shopnight.service.PaymentInfoService;
import com.shopnight.utils.*;
import com.shopnight.vo.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mail.MailSender;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author 13201
* @description 针对表【errand_order】的数据库操作Service实现
* @createDate 2023-04-08 18:19:36
*/
@Service
public class ErrandOrderServiceImpl extends ServiceImpl<ErrandOrderMapper, ErrandOrder>
    implements ErrandOrderService{

    @Autowired
    JavaMailSender mailSender;

    @Autowired
    ErrandOrderMapper errandOrderMapper;
    @Autowired
    UserMapper userMapper;
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Resource(name = "taskExecutor")
    Executor executor;

    private String prefix = "errand-order:";

    @Autowired
    PaymentInfoService paymentInfoService;

    @Autowired
    AllOrderMapper allOrderMapper;
    @Autowired
    AllOrderUtil allOrderUtil;
    @Autowired
    WxPayConfig wxPayConfig;
    @Autowired
    RiderMapper riderMapper;
    @Value("${wx.mini.secret}")
    private String secret;

    @Override
    public JSONObject addErrandOrder(ErrandOrder errandOrder) throws Exception {
        //首先读取用户id 防止瞬间用户id被覆盖
        Integer userId = Integer.valueOf(UserContext.getEndUserId());
        User user = userMapper.selectById(userId);

        String code = new Snowflake().nextIdStr();
        Integer amount = ( errandOrder.getPrice() .multiply(new BigDecimal("100")) ).intValue();
        String response = WxMiniPayUtils.creatOrderJSAPI( user.getOpenId() , code, amount ,ShopIndentConstant.orderTypeEnum.errand.getTyep(), "跑腿支付" );
        JSONObject jsonObject = JSONObject.parseObject(response);
        if (jsonObject.containsKey("prepay_id")){

            Date date = new Date();

            //加入跑腿订单表
            errandOrder.setUserId(userId);
            errandOrder.setCode(code);
            errandOrder.setStatus(ShopIndentConstant.StatusEnum.unpaid.getStatus());
            errandOrder.setCreateTime(date);
            errandOrderMapper.insert(errandOrder);


            //加入订单顺序表
            AllOrder allOrder = new AllOrder();
            allOrder.setUserId(userId);
            allOrder.setOrderId(errandOrder.getId());
            allOrder.setOrderCode(code);
            allOrder.setOrderType(ShopIndentConstant.orderTypeEnum.errand.getTyep());
            allOrder.setOrderStatus(ShopIndentConstant.StatusEnum.unpaid.getStatus());
            allOrder.setOrgId(errandOrder.getOrgId());
            allOrder.setCreateTime(date);
            allOrderMapper.insert(allOrder);


            //已提交腾讯待支付订单
            String prepay_id = jsonObject.get("prepay_id").toString();
            String WxSign = WxSignUtil.getWxAppPayTuneUp(prepay_id);
            JSONObject resJsonObject = JSON.parseObject(WxSign);
          //  resJsonObject.put("errandOrderId",errandOrder.getId());


            //待支付订单存入redis
            stringRedisTemplate.opsForValue().set(prefix + code, JSON.toJSONString(resJsonObject),30, TimeUnit.MINUTES);
            return resJsonObject;
        }

        return null;
    }


    public void errandOrderCallback(String plainText){
        //将明文转换成map
        Gson gson = new Gson();
        HashMap plainTextMap = gson.fromJson(plainText, HashMap.class);
        String orderNo = (String)plainTextMap.get("out_trade_no");
///更改订单状态
/**
 * 根据订单号更新订单状态
 * * @param orderNo
 * @param orderStatus
 */
        //创建queryWrapper
        LambdaQueryWrapper<ErrandOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ErrandOrder::getCode,orderNo);
        ErrandOrder errandOrder = errandOrderMapper.selectOne(queryWrapper);

        if(errandOrder.getStatus() == ShopIndentConstant.StatusEnum.unpaid.getStatus() ){
            errandOrder.setStatus( ShopIndentConstant.StatusEnum.underway.getStatus() );
            errandOrder.setTransactionId(plainTextMap.get("transaction_id").toString());
            errandOrderMapper.update(errandOrder, queryWrapper);
            //更改顺序表状态为进行中
            allOrderUtil.updateAllOrderStatus(errandOrder.getId(),ShopIndentConstant.StatusEnum.underway.getStatus());
            // 异步线程  在支付回调后发送邮件
            CompletableFuture.runAsync(()->{
                // 管理员邮件通知
                try {
                    SendAdminEmail(errandOrder.getOrgId());
                } catch (MessagingException e) {
                    throw new RuntimeException(e);
                }
            },executor);
            //异步线程 删除redis的缓存key
            CompletableFuture.runAsync(()->{
                stringRedisTemplate.delete(prefix + orderNo);
            },executor);
            //异步线程 存储支付日志
            CompletableFuture.runAsync(()->{
                paymentInfoService.createPaymentInfo(plainText);
            },executor);

        }
    }


    public void getUserErandOrderList(List<Object> list, List<Integer> errandIds){
        List<ErrandOrder> errandOrders = errandOrderMapper.selectList(
                new LambdaQueryWrapper<ErrandOrder>()
                        .in(ErrandOrder::getId, errandIds)
        );
        //将errandOrder转换成errandOrderVo 并设置订单类型
        List<UserErrandOrderListVo> collect = errandOrders.stream().map(errandOrder -> {
            UserErrandOrderListVo userErrandOrderListVo = new UserErrandOrderListVo();
            BeanUtils.copyProperties(errandOrder, userErrandOrderListVo);
            userErrandOrderListVo.setOrderType(ShopIndentConstant.orderTypeEnum.errand.getTyep());
            return userErrandOrderListVo;
        }).collect(Collectors.toList());

        list.addAll(collect);
    }

    public void getRiderErandOrderList(List<Object> list, List<Integer> errandIds){
        List<ErrandOrder> errandOrders = errandOrderMapper.selectList(
                new LambdaQueryWrapper<ErrandOrder>()
                        .in(ErrandOrder::getId, errandIds)
        );

        List<Integer> userIds = errandOrders.stream().map(errandOrder -> errandOrder.getUserId()).collect(Collectors.toList());
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().in(User::getUserId, userIds);
        List<User> users = userMapper.selectList(wrapper);

        //将errandOrder转换成errandOrderVo 并设置订单类型
        List<RiderErrandOrderListVo> collect = errandOrders.stream()
                .filter(errandOrder-> errandOrder.getReceivingUserId() == null)
                .map(errandOrder -> {
                    RiderErrandOrderListVo riderErrAndOrderListVo = new RiderErrandOrderListVo();
                    BeanUtils.copyProperties(errandOrder, riderErrAndOrderListVo);
                    riderErrAndOrderListVo.setOrderType(ShopIndentConstant.orderTypeEnum.errand.getTyep());
                    //查询用户信息 赋值进去
                    users.stream().forEach(user->{
                        if (errandOrder.getUserId().toString().equals(user.getUserId().toString()) ){
                            RiderErrandOrderListVo.InitiatorUser initiatorUser = new RiderErrandOrderListVo.InitiatorUser();
                            initiatorUser.setUserNick(user.getUserNick());
                            initiatorUser.setAvatar(user.getAvatar());
                            riderErrAndOrderListVo.setInitiatorUser(initiatorUser);
                        }
                    });
                    return riderErrAndOrderListVo;
                }).collect(Collectors.toList());

        list.addAll(collect);
    }

    public void getReceivingErandOrderList(List<Object> list, List<Integer> errandIds){
        List<ErrandOrder> errandOrders = errandOrderMapper.selectList(
                new LambdaQueryWrapper<ErrandOrder>()
                        .in(ErrandOrder::getId, errandIds)
        );

        List<Integer> userIds = errandOrders.stream().map(errandOrder -> errandOrder.getUserId()).collect(Collectors.toList());
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().in(User::getUserId, userIds);
        List<User> users = userMapper.selectList(wrapper);

        //将errandOrder转换成errandOrderVo 并设置订单类型
        List<RiderErrandOrderListVo> collect = errandOrders.stream()
                .map(errandOrder -> {
                    RiderErrandOrderListVo riderErrAndOrderListVo = new RiderErrandOrderListVo();
                    BeanUtils.copyProperties(errandOrder, riderErrAndOrderListVo);
                    riderErrAndOrderListVo.setOrderType(ShopIndentConstant.orderTypeEnum.errand.getTyep());
                    //查询用户信息 赋值进去
                    users.stream().forEach(user->{
                        if (errandOrder.getUserId().toString().equals(user.getUserId().toString()) ){
                            RiderErrandOrderListVo.InitiatorUser initiatorUser = new RiderErrandOrderListVo.InitiatorUser();
                            initiatorUser.setUserNick(user.getUserNick());
                            initiatorUser.setAvatar(user.getAvatar());
                            riderErrAndOrderListVo.setInitiatorUser(initiatorUser);
                        }
                    });
                    return riderErrAndOrderListVo;
                }).collect(Collectors.toList());

        list.addAll(collect);
    }
    @Override
    public ErrandInfoVo getErrandOrderInfo(Integer errandId) {
        ErrandOrder errandOrder = errandOrderMapper.selectById(errandId);
        ErrandInfoVo errandInfoVo = new ErrandInfoVo();
        BeanUtils.copyProperties(errandOrder,errandInfoVo);

        //如果有接单人 查询接单人信息
        if (errandOrder.getReceivingUserId() != null){
            User user = userMapper.selectById(errandOrder.getReceivingUserId());
            ErrandInfoVo.receivingUserInfo receivingUserInfo = new ErrandInfoVo.receivingUserInfo();
            receivingUserInfo.setAvatar(user.getAvatar());
            receivingUserInfo.setUserNick(user.getUserNick());
            receivingUserInfo.setPhonenumber(user.getPhonenumber());
            errandInfoVo.setReceivingUserInfo(receivingUserInfo);
        }
        //如果为待支付订单 返回待支付时间 已经支付签名
        if (errandInfoVo.getStatus() == ShopIndentConstant.StatusEnum.unpaid.getStatus()){
            //查询redis里 此订单取消的时间
            Long expire = stringRedisTemplate.opsForValue().getOperations().getExpire(prefix + errandInfoVo.getCode());
            String wxpay = stringRedisTemplate.opsForValue().get(prefix + errandInfoVo.getCode());
            errandInfoVo.setWxpay(JSONObject.parseObject(wxpay));
            errandInfoVo.setExpire(expire);
        }

        return errandInfoVo;
    }

    @Override
    public RiderErrandInfoVo getErrandOrderInfoByRider(Integer errandId) {
        ErrandOrder errandOrder = errandOrderMapper.selectById(errandId);
        RiderErrandInfoVo riderErrandInfoVo = new RiderErrandInfoVo();
        BeanUtils.copyProperties(errandOrder,riderErrandInfoVo);
        //查询发起人信息并复制进去
        User initiatorUser = userMapper.selectById(errandOrder.getUserId());
        RiderErrandInfoVo.InitiatorUser initiatorUserInfo = new RiderErrandInfoVo.InitiatorUser();
        BeanUtils.copyProperties(initiatorUser,initiatorUserInfo);
        riderErrandInfoVo.setInitiatorUser(initiatorUserInfo);

        //如果有接单人 查询接单人信息
        if (errandOrder.getReceivingUserId() != null){
            User receivingUser = userMapper.selectById(errandOrder.getReceivingUserId());
            RiderErrandInfoVo.ReceivingUserInfo receivingUserInfo = new RiderErrandInfoVo.ReceivingUserInfo();
            receivingUserInfo.setAvatar(receivingUser.getAvatar());
            receivingUserInfo.setUserNick(receivingUser.getUserNick());
            receivingUserInfo.setPhonenumber(receivingUser.getPhonenumber());
            riderErrandInfoVo.setReceivingUserInfo(receivingUserInfo);
        }


        return riderErrandInfoVo;
    }

    @Override
    public void receivingOrder(Integer userId,Integer orderId) {
        ErrandOrder errandOrder = errandOrderMapper.selectById(orderId);
        errandOrder.setReceivingUserId(userId);
        errandOrder.setUpdateTime(new Date());

        //更新跑腿订单表接单人
        errandOrderMapper.updateById(errandOrder);

        //更新顺序表接单人
        AllOrder allOrder = new AllOrder();
        allOrder.setReceivingUserId(userId);
        allOrderMapper.update(allOrder,new LambdaQueryWrapper<AllOrder>()
                .eq(AllOrder::getOrderId,orderId));

        //异步发送模板通知
        CompletableFuture.runAsync(()->{
            JSONObject body = new JSONObject();
            body.put("touser", userMapper.selectById(errandOrder.getUserId()).getOpenId() );
            body.put("template_id","Y23n6coi3c7rq8Ngkf-lroME3215ROR6ihPS3qS7fpI");
            cn.hutool.json.JSONObject json = new cn.hutool.json.JSONObject();
            Rider rider = riderMapper.selectOne(new LambdaQueryWrapper<Rider>().eq(Rider::getUserId, userId));
            json.set("thing8",new cn.hutool.json.JSONObject().set("value",rider.getName()));
            json.set("time9",new cn.hutool.json.JSONObject().set("value", new SimpleDateFormat("yyyy年MM月dd日 hh:mm").format(new Date()) ));
            json.set("thing5",new cn.hutool.json.JSONObject().set("value","我们将尽快为您完成，谢谢！"));
            body.put("data",json);
            //发送
            String accessToken = getAccessToken();
            String post =  cn.hutool.http.HttpUtil.post("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken, body.toString());

        },executor);

    }

    @Override
    public void riderCompleteOrder(Integer userId,Integer orderType,Integer orderId) {
        ErrandOrder errandOrder = errandOrderMapper.selectOne(new LambdaQueryWrapper<ErrandOrder>().eq(ErrandOrder::getId, orderId));
        errandOrder.setStatus(ShopIndentConstant.StatusEnum.accompLish.getStatus());
        //更新订单状态
        errandOrderMapper.updateById(errandOrder);

        //更新订单顺序表状态
        AllOrder allOrder = new AllOrder();
        allOrder.setOrderStatus(ShopIndentConstant.StatusEnum.accompLish.getStatus());
        allOrderMapper.update(allOrder,
                new LambdaQueryWrapper<AllOrder>()
                        .eq(AllOrder::getOrderType,orderType)
                        .eq(AllOrder::getOrderId,orderId)
        );

        //更新骑手表数据
        Rider rider = riderMapper.selectOne(new LambdaQueryWrapper<Rider>().eq(Rider::getUserId, userId));
        //添加总余额
        rider.setRemainingMoney(rider.getRemainingMoney().add(
                errandOrder.getPrice().multiply(rider.getGetProportion())
        ));
        //添加总单数
        rider.setTotalTakeinOrder(rider.getTotalTakeinOrder() + 1);
        //添加总盈利
        rider.setTotalProfit(rider.getTotalProfit().add(
                errandOrder.getPrice().multiply(rider.getGetProportion())
        ));
        riderMapper.updateById(rider);
    }

    @Override
    @Transactional
    public ApiResult cancelErrandOrder(Integer id) {
        ErrandOrder errandOrder = errandOrderMapper.selectById(id);

        //如果订单状态为待支付
        if (errandOrder.getStatus() == ShopIndentConstant.StatusEnum.unpaid.getStatus()){
            //取消订单
            errandOrder.setStatus(ShopIndentConstant.StatusEnum.cancel.getStatus());
            errandOrderMapper.updateById(errandOrder);
            //更新顺序表
            AllOrder allOrder = new AllOrder();
            allOrder.setOrderStatus(ShopIndentConstant.StatusEnum.cancel.getStatus());
            allOrderMapper.update(allOrder,new LambdaQueryWrapper<AllOrder>()
                    .eq(AllOrder::getOrderId,id)
                    .eq(AllOrder::getOrderType,ShopIndentConstant.orderTypeEnum.errand.getTyep())
            );
            //删除redis里的数据
            stringRedisTemplate.delete(prefix + errandOrder.getCode());
            return ApiResult.ok("");
        }

        //如果订单状态为进行中
        if (errandOrder.getStatus() == ShopIndentConstant.StatusEnum.underway.getStatus()){

            if (errandOrder.getReceivingUserId() != null){
                return ApiResult.fail("订单已被接单，无法取消");
            }

            //取消订单
            errandOrder.setStatus(ShopIndentConstant.StatusEnum.cancel.getStatus());
            errandOrderMapper.updateById(errandOrder);
            //更新顺序表
            AllOrder allOrder = new AllOrder();
            allOrder.setOrderStatus(ShopIndentConstant.StatusEnum.cancel.getStatus());
            allOrderMapper.update(allOrder,new LambdaQueryWrapper<AllOrder>()
                    .eq(AllOrder::getOrderId,id)
                    .eq(AllOrder::getOrderType,ShopIndentConstant.orderTypeEnum.errand.getTyep())
            );

            // 异步退钱
            CompletableFuture.runAsync(() -> {

                JSONObject domesticRefundsJSON = new JSONObject();
                domesticRefundsJSON.put("transaction_id", errandOrder.getTransactionId());
                //雪花算法生成退款订单号
                String outRefundNo = new Snowflake().nextIdStr();
                domesticRefundsJSON.put("out_refund_no", outRefundNo);
                domesticRefundsJSON.put("reason","取消校园跑腿订单");
                JSONObject amount = new JSONObject();
                amount.put("refund", (errandOrder.getPrice().multiply(new BigDecimal("100"))).intValue()  );
                amount.put("total",  (errandOrder.getPrice().multiply(new BigDecimal("100"))).intValue());
                amount.put("currency","CNY");
                domesticRefundsJSON.put("amount",amount);
                domesticRefundsJSON.put("notify_url", wxPayConfig.getRefundsNotifyDomain() );
                String parm = domesticRefundsJSON.toJSONString();
                try {
                 /*   String domesticRefundsBody =*/
                 WxMiniPayUtils.v3Post("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds",parm);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

            }, executor);


               return ApiResult.ok("取消成功 资金稍后将原路返回");
            }


        return ApiResult.fail("订单此状态无法取消");
    }


    public void  SendAdminEmail(Integer orgId) throws MessagingException {

//管理员邮件通知

        //随机图片的地址
        String[] img = {
                "https://bloggerdisk.oss-cn-hangzhou.aliyuncs.com/image/email/2014278.jpg",
                "https://bloggerdisk.oss-cn-hangzhou.aliyuncs.com/image/email/2035567.jpg",
                "https://bloggerdisk.oss-cn-hangzhou.aliyuncs.com/image/email/2046017.jpg",
                "https://bloggerdisk.oss-cn-hangzhou.aliyuncs.com/image/email/2052796.jpg",
                "https://bloggerdisk.oss-cn-hangzhou.aliyuncs.com/image/email/2053919.jpg",
                "https://bloggerdisk.oss-cn-hangzhou.aliyuncs.com/image/email/2053942.jpg",
                "https://bloggerdisk.oss-cn-hangzhou.aliyuncs.com/image/email/2054350.jpg",
                "https://bloggerdisk.oss-cn-hangzhou.aliyuncs.com/image/email/2054480.jpg",
                "https://bloggerdisk.oss-cn-hangzhou.aliyuncs.com/image/email/2054618.jpg",
                "https://bloggerdisk.oss-cn-hangzhou.aliyuncs.com/image/email/2054956.jpg"
        };

        //根据id查询用户信息
        riderMapper.selectList(
                        new LambdaQueryWrapper<Rider>()
                                .eq(Rider::getOrgId,orgId)
                                .eq(Rider::getReceiveEmail,1)
                                .eq(Rider::getState,1)
                ).parallelStream()
                .filter(rider-> StringUtils.hasLength(userMapper.selectById(rider.getUserId()).getEmail()))
                .forEach( rider ->{
                    User user = userMapper.selectById(rider.getUserId());
                    try {
                        //创建邮件消息
                        MimeMessage message = mailSender.createMimeMessage();
                        MimeMessageHelper helper = new MimeMessageHelper(message, true);
                        //谁发的
                        helper.setFrom("15555881410@163.com");
                        //谁要接收
                        helper.setTo(user.getEmail());
                        //邮件标题
                        helper.setSubject("来新订单了！");
                        String html = EmailHtml.RiderEmailHtml(
                                img[new Random().nextInt(10)],  //随机图片
                                rider.getName(),
                                "校园跑腿： ",
                                "请于小程序接单大厅查看"
                        );
                        //设置内容
                        helper.setText(html, true);
                        mailSender.send(message);

                    } catch (MessagingException e) {
                        throw new RuntimeException(e);
                    }
                });
    }

    public String getAccessToken() {
        String appId = wxPayConfig.getAppid();
        String appSecret = secret;
        String result = cn.hutool.http.HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret);
        cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(result);
        return jsonObject.getStr("access_token");
    }


}




